// Author: wsfuyibing <682805@qq.com>
// Date: 2025-04-27

package config

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"gitee.com/go-libs/config"
	"gitee.com/go-libs/runtime"
	"strings"
)

// Encryption
// 加密解密实例.
var Encryption EncryptionConfig

type (
	// EncryptionConfig
	// 加密解密接口.
	EncryptionConfig interface {
		GetPublic() string
		GetPublicKey() (*rsa.PublicKey, error)
		GetPrivate() string
		GetPrivateKey() (*rsa.PrivateKey, error)
	}

	encryptionConfig struct {
		err        error           `yaml:"-"`
		Public     string          `yaml:"public"`
		PublicKey  *rsa.PublicKey  `yaml:"-"`
		Private    string          `yaml:"private"`
		PrivateKey *rsa.PrivateKey `yaml:"-"`
	}
)

// +---------------------------------------------------------------------------+
// | Interface methods                                                         |
// +---------------------------------------------------------------------------+

func (o *encryptionConfig) GetPublic() string                       { return o.Public }
func (o *encryptionConfig) GetPublicKey() (*rsa.PublicKey, error)   { return o.PublicKey, o.err }
func (o *encryptionConfig) GetPrivate() string                      { return o.Private }
func (o *encryptionConfig) GetPrivateKey() (*rsa.PrivateKey, error) { return o.PrivateKey, o.err }

// +---------------------------------------------------------------------------+
// | Hook methods                                                              |
// +---------------------------------------------------------------------------+

func (o *encryptionConfig) After() {
	// 1. 必须配置.
	if o.Public == "" || o.Private == "" {
		o.err = runtime.NewError(500, "certification lost")
		return
	}

	// 2. 解析密钥.
	if o.parsePrivate(); o.err == nil {
		o.parsePublic()
	}
}

// +---------------------------------------------------------------------------+
// | Access methods                                                            |
// +---------------------------------------------------------------------------+

func (o *encryptionConfig) init() *encryptionConfig {
	_ = config.Seek("encryption.yaml", "encryption.yml").ScanYaml(o)
	return o
}

func (o *encryptionConfig) parsePrivate() {
	var (
		block *pem.Block
		body  = []byte(strings.TrimSpace(o.Private))
		err   error
		ok    bool
		tmp   any
	)

	// 1. 解析密钥.
	if block, _ = pem.Decode(body); block == nil {
		o.err = fmt.Errorf("certification for private key not malformed")
		return
	}

	// 2. 校验密钥.
	if o.PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
		if tmp, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
			o.err = err
			return
		}

		if o.PrivateKey, ok = tmp.(*rsa.PrivateKey); !ok {
			o.err = fmt.Errorf("certification for private key not translated")
		}
	}
	return
}

func (o *encryptionConfig) parsePublic() {
	var (
		block  *pem.Block
		body   = []byte(strings.TrimSpace(o.Public))
		cert   *x509.Certificate
		err    error
		ok     bool
		parsed any
	)

	// 1. 解析密钥.
	if block, _ = pem.Decode(body); block == nil {
		o.err = fmt.Errorf("certification for public key not malformed")
		return
	}

	// 2. 校验密钥.
	if parsed, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
		if cert, err = x509.ParseCertificate(block.Bytes); err != nil {
			o.err = err
			return
		}
		parsed = cert.PublicKey
	}

	// 3. 格式转换
	if o.PublicKey, ok = parsed.(*rsa.PublicKey); !ok {
		o.err = fmt.Errorf("certification for public key not translated")
	}
	return
}

func (o *encryptionConfig) parsePrivate2() (key *rsa.PrivateKey, err error) {
	var (
		block *pem.Block
		ok    bool
		tmp   any
	)

	if block, _ = pem.Decode([]byte(o.Private)); block == nil {
		err = fmt.Errorf("certification for private key not malformed")
		return
	}

	if key, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
		if tmp, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
			return
		}

		if key, ok = tmp.(*rsa.PrivateKey); !ok {
			err = fmt.Errorf("certification for private key not translated")
		}
	}
	return
}

func (o *encryptionConfig) parsePublic2() (key *rsa.PublicKey, err error) {
	var (
		block  *pem.Block
		cert   *x509.Certificate
		ok     bool
		parsed any
	)

	if block, _ = pem.Decode([]byte(o.Public)); block == nil {
		err = fmt.Errorf("certification for public key not malformed")
		return
	}

	if parsed, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
		if cert, err = x509.ParseCertificate(block.Bytes); err != nil {
			return
		}
		parsed = cert.PublicKey
	}

	if key, ok = parsed.(*rsa.PublicKey); !ok {
		err = fmt.Errorf("certification for public key not translated")
	}
	return
}
